#include <QJsonObject>
#include <QJsonArray>

#include <osg/Geometry>
#include <osg/BlendFunc>

#include <BRepBuilderAPI_MakeFace.hxx>
#include <GC_MakeCircle.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Face.hxx>
#include <gp_Cylinder.hxx>
#include <gp_Cone.hxx>

#include "MultiSectionCylinder.h"
#include "GetTriangles.h"
#include "JsonReader.h"
#include "Surface.h"
#include "OsgUtil.h"
#include "OcctOsg.h"

struct CylinderSection {
	double bottomRadius;
	double topRadius;
	double height;
};

void tryCombineLast2Section(std::vector<CylinderSection>& sections)
{
	int sectionCount = sections.size();
	if (sectionCount < 2)
		return;
	CylinderSection sectionBelow = sections[sectionCount - 2];
	double scale1 = (sectionBelow.bottomRadius - sectionBelow.topRadius) / sectionBelow.height;
	CylinderSection sectionAbove = sections[sectionCount - 1];
	double scale2 = (sectionAbove.bottomRadius - sectionAbove.topRadius) / sectionAbove.height;
	//合并相似的section
	if ((sectionBelow.topRadius == sectionAbove.bottomRadius) && (abs(scale1 - scale2) <= 0.0001))
	{
		CylinderSection section;
		section.bottomRadius = sectionBelow.bottomRadius;
		section.topRadius = sectionAbove.topRadius;
		section.height = sectionAbove.height + sectionBelow.height;
		sections.resize(sectionCount - 1);
		sections[sectionCount - 2] = section;
	}
}

struct MultiSectionCylinderPrivate
{
	MultiSectionCylinderPrivate()
		: m_rep3D(new osg::Geometry())
	{
		m_rep3D->setNodeMask(D3SurfaceMask);
		auto ss = m_rep3D->getOrCreateStateSet();
		ss->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
		ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
		static osg::ref_ptr<osg::BlendFunc> blendFunc = new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		ss->setAttributeAndModes(blendFunc);
	}
	osg::ref_ptr<osg::Geometry> m_rep3D;
	osg::Vec3d m_bottomCenter;
	osg::Vec3d m_axis = osg::Vec3d(0, 0, 1);
	std::vector<CylinderSection> m_sections;
	bool m_selected = false;
};

MultiSectionCylinder::MultiSectionCylinder(INode* parent)
	: Ent3DFace(parent)
	, d_ptr(new MultiSectionCylinderPrivate)
{
}

void MultiSectionCylinder::setBottomCenter(const osg::Vec3d& pos)
{
	d_ptr->m_bottomCenter = pos;
	addFlags(UPDATE_GEOMETRY);
}

void MultiSectionCylinder::setAxis(const osg::Vec3d & axis)
{
	osg::Vec3d a(axis);
	double len = a.normalize();
	if (len > 0)
	{
		d_ptr->m_axis = a;
		addFlags(UPDATE_GEOMETRY);
	}
}

void MultiSectionCylinder::addSection(double bottomRadius, double topRadius, double height)
{
	d_ptr->m_sections.push_back({ bottomRadius, topRadius, height });
	tryCombineLast2Section(d_ptr->m_sections);
	addFlags(UPDATE_GEOMETRY);
}

int MultiSectionCylinder::sectionCount()
{
	return d_ptr->m_sections.size();
}

void setTrianglesNormals(osg::Geometry& geoset);
void MultiSectionCylinder::update3D()
{
	if (flags())
	{
		const int circlePonitsCount = 90;
		auto va = new osg::Vec3Array();
		if (d_ptr->m_sections.size() == 1 )
		{
			//说明是一段
			 auto bottomCirclePoints = getCirclePoints(d_ptr->m_bottomCenter, d_ptr->m_axis, d_ptr->m_sections[0].bottomRadius, circlePonitsCount);
			 osg::Vec3d topCenter(d_ptr->m_bottomCenter);
			 topCenter += d_ptr->m_axis * d_ptr->m_sections[0].height;
			 auto topCirclePoints = getCirclePoints(topCenter, d_ptr->m_axis, d_ptr->m_sections[0].topRadius, circlePonitsCount);
			 int bottomCirclePointCount = bottomCirclePoints.size();
			 int topCirclePointCount = topCirclePoints.size();
			 //底面
			 if (bottomCirclePointCount > 0)
			 {
				 auto bottomTriangles = getDiskTriangles(d_ptr->m_bottomCenter, bottomCirclePoints, true);
				 appendPoints(va, bottomTriangles);
			 }
			 if (topCirclePointCount > 0)
			 {
				 auto topTriangles = getDiskTriangles(topCenter, topCirclePoints, false);
				 appendPoints(va, topTriangles);
			 }
			 //侧面
			 std::vector<osg::Vec3d> sideTriangles;
			 if (bottomCirclePointCount == topCirclePointCount)
			 {
				 sideTriangles =  getCylinderSideTriangles(bottomCirclePoints,topCirclePoints);
			 }
			 else if (bottomCirclePointCount == 0)
			 {
				 sideTriangles = getConeSideTriangles(d_ptr->m_bottomCenter, topCirclePoints, true);
			 }
			 else if (topCirclePointCount == 0)
			 {
				 sideTriangles = getConeSideTriangles(topCenter, bottomCirclePoints);
			 }
			 appendPoints(va, sideTriangles);
		}
		if (d_ptr->m_sections.size() > 1)
		{
			//说明是多段圆柱体
			int sectionCount = d_ptr->m_sections.size();
			std::vector<osg::Vec3d> bottomCirclePoints;
			std::vector<osg::Vec3d> topCirclePoints;
			std::vector<osg::Vec3d> nextBottomCirclePoint;
			osg::Vec3d bottomCenter(d_ptr->m_bottomCenter);
			osg::Vec3d topCenter(d_ptr->m_bottomCenter);
			int bottomCirclePointCount;
			int topCirclePointCount;
			int nextBottomCirclePointCount;
			//底端面
			bottomCirclePoints = getCirclePoints(bottomCenter, d_ptr->m_axis, d_ptr->m_sections[0].bottomRadius, circlePonitsCount);
			bottomCirclePointCount = bottomCirclePoints.size();
			if (bottomCirclePointCount > 0)
			{
				auto bottomTriangles = getDiskTriangles(bottomCenter, bottomCirclePoints, true);
				appendPoints(va, bottomTriangles);
			}
			//中间段
			for (int i = 0; i < sectionCount - 1; i++)
			{
				bottomCenter = topCenter;
				bottomCirclePoints = getCirclePoints(bottomCenter, d_ptr->m_axis, d_ptr->m_sections[i].bottomRadius, circlePonitsCount);
				bottomCirclePointCount = bottomCirclePoints.size();
				topCenter += d_ptr->m_axis * d_ptr->m_sections[i].height;
				topCirclePoints = getCirclePoints(topCenter, d_ptr->m_axis, d_ptr->m_sections[i].topRadius, circlePonitsCount);
				topCirclePointCount = topCirclePoints.size();
				nextBottomCirclePoint = getCirclePoints(topCenter, d_ptr->m_axis, d_ptr->m_sections[i + 1].bottomRadius, circlePonitsCount);
				nextBottomCirclePointCount = nextBottomCirclePoint.size();
				//侧面
				std::vector<osg::Vec3d> sideTriangles;
				if (bottomCirclePointCount == topCirclePointCount)
				{
					sideTriangles = getCylinderSideTriangles(bottomCirclePoints, topCirclePoints);
				}
				else if (bottomCirclePointCount == 0)
				{
					sideTriangles = getConeSideTriangles(bottomCenter, topCirclePoints, true);
				}
				else if (topCirclePointCount == 0)
				{
					sideTriangles = getConeSideTriangles(topCenter, bottomCirclePoints);
				}
				appendPoints(va, sideTriangles);
				//中间面
				std::vector<osg::Vec3d> cirqueTriangles;
				if ((topCirclePointCount > 0) && (nextBottomCirclePointCount > 0))
				{
					if (d_ptr->m_sections[i].topRadius > d_ptr->m_sections[i + 1].bottomRadius)
					{
						cirqueTriangles = getCirqueTriangles(topCirclePoints, nextBottomCirclePoint, false);
					}
					else if (d_ptr->m_sections[i].topRadius < d_ptr->m_sections[i + 1].bottomRadius)
					{
						cirqueTriangles = getCirqueTriangles(nextBottomCirclePoint, topCirclePoints, true);
					}
					appendPoints(va, cirqueTriangles);
				}
				else
				{
					if (nextBottomCirclePointCount > 0)
					{
						auto nextBottomTriangles = getDiskTriangles(topCenter, nextBottomCirclePoint, true);
						appendPoints(va, nextBottomTriangles);
					}
					if (topCirclePointCount > 0)
					{
						auto topTriangles = getDiskTriangles(topCenter, topCirclePoints, false);
						appendPoints(va, topTriangles);
					}
				}
			}
			//顶端面和顶侧面
			bottomCirclePoints = nextBottomCirclePoint;
			bottomCirclePointCount = bottomCirclePoints.size();
			bottomCenter = topCenter;
			topCenter += d_ptr->m_axis * d_ptr->m_sections[sectionCount - 1].height;
			topCirclePoints = getCirclePoints(topCenter, d_ptr->m_axis, d_ptr->m_sections[sectionCount - 1].topRadius, circlePonitsCount);
			topCirclePointCount = topCirclePoints.size();
			if (topCirclePointCount > 0)
			{
				auto topTriangles = getDiskTriangles(topCenter, topCirclePoints, false);
				appendPoints(va, topTriangles);
			}
			std::vector<osg::Vec3d> sideTriangles;
			if (bottomCirclePointCount == topCirclePointCount)
			{
				sideTriangles = getCylinderSideTriangles(bottomCirclePoints, topCirclePoints);
			}
			else if (bottomCirclePointCount == 0)
			{
				sideTriangles = getConeSideTriangles(bottomCenter, topCirclePoints, true);
			}
			else if (topCirclePointCount == 0)
			{
				sideTriangles = getConeSideTriangles(topCenter, bottomCirclePoints);
			}
			appendPoints(va, sideTriangles);
		}
			// 生成osg对象
			const osg::Vec4f& selectedColor = osg::Vec4f(0.f, 0.8f, 0.f, 1.0f);
			d_ptr->m_rep3D->setVertexArray(va);

			osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array();

			if (d_ptr->m_selected)
				ca->push_back(g_selectedFaceColor);
			else
				ca->push_back(g_faceColor);

			d_ptr->m_rep3D->setColorArray(ca.get(), osg::Array::BIND_OVERALL);

			d_ptr->m_rep3D->getPrimitiveSetList().clear();
			d_ptr->m_rep3D->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, va->size()));

			//osgUtil::SmoothingVisitor::smooth(*(d_ptr->m_rep3D.get()));//自动生成法线
			setTrianglesNormals(*d_ptr->m_rep3D);
			d_ptr->m_rep3D->dirtyGLObjects();
			removeFlags(0xFFFFFFFF);
		}
}

osg::Node * MultiSectionCylinder::getRep3D() const
{
	return d_ptr->m_rep3D.get();
}

void MultiSectionCylinder::setSelected(bool val)
{
	if (d_ptr->m_selected != val)
	{
		d_ptr->m_selected = val;
		addFlags(UPDATE_GEOMETRY);
	}
}

void MultiSectionCylinder::save(QJsonObject & json) const
{
	Ent::save(json);

	json["bottomCenter"] = saveVec3f(d_ptr->m_bottomCenter);
	json["axis"] = saveVec3f(d_ptr->m_axis);
	QJsonArray array;
	for (auto section : d_ptr->m_sections)
	{
		QJsonObject jsonObject;
		jsonObject.insert("bottomRadius", section.bottomRadius);
		jsonObject.insert("topRadius", section.topRadius);
		jsonObject.insert("height", section.height);
		array.append(jsonObject);
	}
	json["sections"] = array;
}

void MultiSectionCylinder::load(const QJsonObject & json, JsonReader & reader)
{
	Ent::load(json, reader);

	d_ptr->m_bottomCenter = loadVec3f(json["bottomCenter"].toArray());
	if (json.contains("axis"))
		d_ptr->m_axis = loadVec3f(json["axis"].toArray());
	for (const auto& object : json["sections"].toArray())
	{
		auto sectionJson = object.toObject();
		CylinderSection section = { sectionJson["bottomRadius"].toDouble(), sectionJson["topRadius"].toDouble(), sectionJson["height"].toDouble() };
		d_ptr->m_sections.push_back(section);
	}
}

std::vector<osg::Vec3d> MultiSectionCylinder::handles() const
{
	std::vector<osg::Vec3d> result;
	result.push_back(d_ptr->m_bottomCenter);
	double heightTotal = 0.f;
	for (auto ele:d_ptr->m_sections)
	{
		heightTotal += ele.height;
	}
	osg::Vec3d topCenter(d_ptr->m_bottomCenter);
	topCenter += d_ptr->m_axis * heightTotal;
	result.push_back(topCenter);
	return result;
}

void MultiSectionCylinder::move(const osg::Vec3d & offset)
{
	d_ptr->m_bottomCenter += offset;
	addFlags(UPDATE_GEOMETRY);
}

std::shared_ptr<Ent> MultiSectionCylinder::clone() const
{
	auto cylinderPlural = std::make_shared<MultiSectionCylinder>(getParent());
	cylinderPlural->copy(*this);
	return cylinderPlural;
}

void MultiSectionCylinder::copy(const Ent & ent)
{
	auto cylinderPlural = dynamic_cast<const MultiSectionCylinder*>(&ent);
	if (cylinderPlural)
	{
		d_ptr->m_bottomCenter = cylinderPlural->d_ptr->m_bottomCenter;
		d_ptr->m_axis = cylinderPlural->d_ptr->m_axis;
		d_ptr->m_sections = cylinderPlural->d_ptr->m_sections;
	}
}
std::shared_ptr<Surface> makeCirFace(const TopoDS_Wire& cirWire,INode* parent)
{
	BRepBuilderAPI_MakeFace maker(cirWire);
	if (maker.IsDone())
	{
		TopoDS_Face face = maker.Face();
		auto surface = std::make_shared<Surface>(parent);
		surface->setShape(face);
		return surface;
	}
	else
	{
		return nullptr;
	}
}

std::set<std::shared_ptr<INode>> MultiSectionCylinder::decompose() const
{
	std::set<std::shared_ptr<INode>> result;
	int sectionCount = d_ptr->m_sections.size();
	osg::Vec3d xAxis, yAxis;
	ArbitraryAxis(d_ptr->m_axis, xAxis, yAxis);
	gp_Dir vDir = toDir(d_ptr->m_axis);
	gp_Dir Vx = toDir(xAxis);
	gp_Pnt bottomCenter;
	gp_Pnt topCenter;
	topCenter.SetCoord(d_ptr->m_bottomCenter.x(), d_ptr->m_bottomCenter.y(), d_ptr->m_bottomCenter.z());
	TopoDS_Wire bottomWire;
	TopoDS_Wire topWire;
	for (int i = 0; i < sectionCount; i++)
	{
		bottomCenter = topCenter;
		auto bottomCir = GC_MakeCircle(bottomCenter, vDir, d_ptr->m_sections[i].bottomRadius).Value();
		auto bottomEdge = BRepBuilderAPI_MakeEdge(bottomCir).Edge();
		bottomWire = BRepBuilderAPI_MakeWire(bottomEdge).Wire();
		if (i == 0 && d_ptr->m_sections[0].bottomRadius != 0.0)
		{
			auto surface = makeCirFace(bottomWire, getParent());
			if (surface)
			{
				surface->reverse();
				result.insert(surface);
			}
		}
		//两圆台相交面
		if (i - 1 >= 0)
		{
			if (d_ptr->m_sections[i - 1].topRadius != d_ptr->m_sections[i].bottomRadius)
			{
				if (d_ptr->m_sections[i - 1].topRadius == 0.0&&d_ptr->m_sections[i].bottomRadius != 0.0)
				{
					auto surface = makeCirFace(bottomWire, getParent());
					if (surface)
					{
						surface->reverse();
						result.insert(surface);
					}
				}
				else if (d_ptr->m_sections[i - 1].topRadius != 0.0&&d_ptr->m_sections[i].bottomRadius == 0.0)
				{
					auto surface = makeCirFace(topWire, getParent());
					if (surface)
					{
						result.insert(surface);
					}
				}
				else if (d_ptr->m_sections[i - 1].topRadius != 0.0&&d_ptr->m_sections[i].bottomRadius != 0.0)
				{
					if (d_ptr->m_sections[i - 1].topRadius > d_ptr->m_sections[i].bottomRadius)
					{
						TopoDS_Face cirFace = BRepBuilderAPI_MakeFace(topWire);
						BRepBuilderAPI_MakeFace maker(cirFace);
						bottomWire.Reverse();
						maker.Add(bottomWire);
						bottomWire.Reverse();
						auto face = maker.Face();
						auto surface = std::make_shared<Surface>(getParent());
						surface->setShape(face);
						result.insert(surface);
					}
					else
					{
						TopoDS_Face cirFace = BRepBuilderAPI_MakeFace(bottomWire);
						BRepBuilderAPI_MakeFace maker(cirFace);
						topWire.Reverse();
						maker.Add(topWire);
						auto face = maker.Face();
						face.Reverse();
						auto surface = std::make_shared<Surface>(getParent());
						surface->setShape(face);
						result.insert(surface);
					}
				}
			}
		}
		topCenter = bottomCenter.Translated(vDir.XYZ() * d_ptr->m_sections[i].height);
		auto topCir = GC_MakeCircle(topCenter, vDir, d_ptr->m_sections[i].topRadius).Value();
		auto topEdge = BRepBuilderAPI_MakeEdge(topCir).Edge();
		topWire = BRepBuilderAPI_MakeWire(topEdge).Wire();
		if (i == sectionCount - 1 && d_ptr->m_sections[i].topRadius != 0.0)
		{
			auto surface = makeCirFace(topWire, getParent());
			if (surface)
			{
				result.insert(surface);
			}
		}
		//圆台侧面
		auto surface = std::make_shared<Surface>(getParent());
		if (std::abs(d_ptr->m_sections[i].bottomRadius - d_ptr->m_sections[i].topRadius) > 0.0001)
		{
			double dx = d_ptr->m_sections[i].topRadius - d_ptr->m_sections[i].bottomRadius;
			double dz = d_ptr->m_sections[i].bottomRadius * d_ptr->m_sections[i].height / dx;
			gp_Pnt origin = bottomCenter.Translated(vDir.XYZ() * -dz);
			gp_Ax3 ax3(origin, vDir, Vx);
			double ang = atan(dx / d_ptr->m_sections[i].height);
			gp_Cone cone(ax3, ang,0);
			double v1 = d_ptr->m_sections[i].bottomRadius / sin(ang);
			double v2 = d_ptr->m_sections[i].topRadius / sin(ang);
			auto face = BRepBuilderAPI_MakeFace(cone, 0, 2 * M_PI, v1, v2).Face();
			surface->setShape(face);
		}
		else 
		{
			gp_Ax3 ax3(bottomCenter, vDir, Vx);
			gp_Cylinder cylinder(ax3, d_ptr->m_sections[i].bottomRadius);
			auto face = BRepBuilderAPI_MakeFace(cylinder, 0, 2 * M_PI, 0, d_ptr->m_sections[i].height).Face();
			surface->setShape(face);
		}
		result.insert(surface);
	}
	return result;
}

void MultiSectionCylinder::rotate(const double angle, const osg::Vec2d & basePoint)
{
	rotate2D(basePoint.x(), basePoint.y(), angle, d_ptr->m_bottomCenter);
	rotate2D(0, 0, angle, d_ptr->m_axis);
	addFlags(UPDATE_GEOMETRY);
}
